/* Copyright 2007  Alan Nisota <alannisota@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include "r5000/r5000.h"

enum {
  TOGGLE_POWER,
  CHANGE_CHANNEL,
  READ_STREAM,
  MULTI_READ,
  MULTI_OPEN,
  OPEN_CLOSE,
  SCAN_DEVICES,
  TURN_ON,
  TURN_OFF,
  CHECK_POWER,
  MAX_CMD,
};
char cmd_names[][20] = {
  {"TOGGLE_POWER"},
  {"CHANGE_CHANNEL"},
  {"READ_STREAM"},
  {"MULTI_READ"},
  {"MULTI_OPEN"},
  {"OPEN_CLOSE"},
  {"SCAN_DEVICES"},
  {"TURN_ON"},
  {"TURN_OFF"},
  {"CHECK_POWER"},
};

char stb_names[][20] = {
  {"vip211"},
  {"directv"},
  {"hdd"},
  {"dsr"},
  {"vip622"},
  {"dish6000"},
  {0},
};

extern char strmfile[255];
void help(char *cmdline) {
  int i;
  printf("%s <-t type> <--m command> [--c channel] [--s serial] [-h]\n", cmdline);
  printf("\t-t/--type type     : set STB type to type.  'type' can be either\n");
  printf("\t                     a numerical value or string.  Valid types are:\n");
  for(i = 0; stb_names[i][0] != 0; i++) {
    printf("\t\t%d: %s\n", i, stb_names[i]);
  }
  printf("\t-m/--cmd command   : set command to execute.  'command' can be either\n");
  printf("\t                     a numerical value or string.  Valid commands are:\n");
  for(i = 0; i < MAX_CMD; i++) {
    printf("\t\t%d: %s\n", i, cmd_names[i]);
  }
  printf("\t-c/--channel       : set channel to read (this does NOT tune\n");
  printf("\t-s/--serial serial : set serial number\n");
  printf("\t-h/--help          : show this help message\n");
  exit(0);
}

void printstr(char *str)
{
  fprintf(stderr, "%s", str);
}

static unsigned char buffer[1000189];
static unsigned char *ptr = buffer;
unsigned int r5000_device_tspacket_handler(unsigned char *tspacket, int len, void *callback_data)
{
    int fd = *(int *) callback_data;
    if (len <= 0)
      return 0;
    if(memcpy(ptr, tspacket, len));
    ptr+=len;
    if(ptr-buffer > 1000000) {
      write(fd, buffer, ptr-buffer);
      ptr = buffer;
    }
    return 1;
}

int main(int argc, char *argv[])
{
  r5kdev_t *usbdev = NULL;
  int glblfd, i;
  r5ktype_t stb = R5K_STB_MAX;
  int cmd = -1;
  const char *channel = NULL;
  const char *serial = NULL;
  int runtime = 10;
  struct option long_options[] = {
    {"type", required_argument, NULL, 't'},
    {"cmd", required_argument, NULL, 'm'},
    {"channel", required_argument, NULL, 'c'},
    {"serial", required_argument, NULL, 's'},
    {"time", required_argument, NULL, 'i'},
    {"dbgfile", required_argument, NULL, 'D'},
    {"help", no_argument , NULL, 'h'},
    {0, 0, 0, 0}
  };
  while (1) {
    char c;
    c = getopt_long (argc, argv,
                     "t:m:c:s:hD:i:",
                     long_options, NULL);
    if(c == EOF)
      break;
    switch(c) {
      case 't':
      {
        char *ptr;
        int i;
        stb = (r5ktype_t)strtol(optarg, &ptr, 10);
        if(ptr == optarg) {
          stb = R5K_STB_MAX;
          for(i = 0; stb_names[i][0] != 0; i++) {
            if(strncasecmp(optarg, stb_names[i], strlen(stb_names[i])) == 0) {
              stb = (r5ktype_t)i;
              break;
            }
          }
          if(stb == R5K_STB_MAX) {
            fprintf(stderr, "Unknown STB type: %s\n", optarg);
            exit(-1);
          }
        } else if(stb >= R5K_STB_MAX) {
          fprintf(stderr, "Illegal STB type: %d\n", stb);
          exit(-1);
        }
        break;
      }
      case 'm':
      {
        char *ptr;
        int i;
        cmd = strtol(optarg, &ptr, 10);
        if(ptr == optarg) {
          cmd = -1;
          for(i = 0; i < MAX_CMD; i++) {
            if(strncasecmp(optarg, cmd_names[i], strlen(cmd_names[i])) == 0) {
              cmd = i;
              break;
            }
          }
          if(cmd == -1) {
            fprintf(stderr, "Unknown command: %s\n", optarg);
            exit(-1);
          }
        } else if(cmd < 0 || cmd >= MAX_CMD) {
          fprintf(stderr, "Illegal command: %d\n", cmd);
          exit(-1);
        }
        break;
      }
      case 'c':
      {
        int ok = 1;
        char *p;
        if(! strlen(optarg)) {
          fprintf(stderr, "Couldn't parse channel '%s'\n", optarg);
          ok = 0;
        }
        for(p = optarg; *p; p++) {
          if(*p != 'P' && *p != ',' && *p != '-' && (*p < '0' || *p > '9')) {
            fprintf(stderr, "Couldn't parse channel '%s'\n", optarg);
            ok = 0;
            break;
          }
        }
        if(ok)
          channel = optarg;
        break;
      }
      case 'i':
      {
        char *ptr;
        runtime = strtol(optarg, &ptr, 0);
        if(optarg == ptr) {
          fprintf(stderr, "Couldn't parse time '%s'\n", optarg);
        }
        break;
      }
      case 's':
        serial = optarg;
        break;
      case 'h':
        help(argv[0]);
        break;
      case 'D':
        #ifdef R5K_DEBUG
          strncpy(strmfile, optarg, 255);
        #else
          fprintf(stderr, "--dbgfile only supported in debug mode.\nMake sure you know what you are doing!\n");
          exit(-1);
        #endif
    }
  }
  if(cmd == -1 || stb == R5K_STB_MAX) {
    fprintf(stderr, "Must specify --cmd and --stb\n");
    exit(-1);
  }
  r5000_init(printstr);
  if(cmd == SCAN_DEVICES) {
    r5kenum_t devs;
    printf("Scanning for R5000 devices\n");
    if(! r5000_find_stbs(&devs)) {
      printf("Failed to initialize r5000 devices\n");
    }
    for(i=0; i < devs.count; i++) {
      printf("Found: %s\n", devs.serial[i]);
    }
    goto end;
  }
  usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
  if(! usbdev) {
    fprintf(stderr, "R5000 device could not be found/opened\n");
    exit(-1);
  }
  glblfd=open("raw.ts", O_WRONLY | O_TRUNC | O_CREAT, 0666);
  if(cmd == TOGGLE_POWER) {
    int new_state;
    printf("Toggling On/Off\n");
    new_state = r5000_toggle_on_off(usbdev);
    printf("Turned power %s\n", new_state ? "On" : "Off");
  }
  if(cmd == TURN_ON) {
    int new_state;
    printf("Turning STB On\n");
    new_state = r5000_power_on_off(usbdev, 1);
    printf("Turned power %s\n", new_state ? "On" : "Off");
  }
  if(cmd == TURN_OFF) {
    int new_state;
    printf("Turning STB Off\n");
    new_state = r5000_power_on_off(usbdev, 0);
    printf("Turned power %s\n", new_state ? "On" : "Off");
  }
  if(cmd == CHECK_POWER) {
    int new_state;
    new_state = r5000_get_power_state(usbdev);
    printf("Power is currently %s\n", new_state ? "On" : "Off");
  }
  if(cmd == CHANGE_CHANNEL) {
    if(! channel) {
      printf("Must specify a channel when using '-m CHANGE_CHANNEL'\n");
      exit(-1);
    }
    printf("Setting channel %s\n", channel);
    r5000_change_channel(usbdev, channel, 0);

/*
    usleep(runtime * 1000);
    r5000_start_stream(usbdev);
    time_t t = time(NULL);
    while (time(NULL) - t < 7)
    {
      // This will timeout after 1ms regardless of data availability
      //usleep(10000);
      r5000_loop_iterate(usbdev, 10);
    }
    r5000_stop_stream(usbdev);
*/

  }
  if(cmd == OPEN_CLOSE) {
    int on_off;
    printf("Doing open/close\n");
    on_off = r5000_get_power_state(usbdev);
    printf("State1: %d\n", on_off);
    r5000_close(usbdev);
    printf("Closed\n");
    sleep(1);
    usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
    printf("ReOpened\n");
    on_off = r5000_get_power_state(usbdev);
    printf("State2: %d\n", on_off);
  }
  if(cmd == READ_STREAM || cmd == MULTI_READ) {
    if(channel) {
      uint c;
      char *ptr;
      printf("Setting channel %s\n", channel);
      c = strtol(channel, &ptr, 0);
      r5000_change_channel(usbdev, NULL, c);
    }
    printf("Reading stream\n");
    r5000_start_stream(usbdev);
    time_t t = time(NULL);
    while (time(NULL) - t < runtime)
    {
      // This will timeout after 1ms regardless of data availability
      //usleep(10000);
      r5000_loop_iterate(usbdev, 10);
    }
    r5000_stop_stream(usbdev);
  }
  if(cmd == MULTI_READ) {
    printf("Reading stream again\n");
    close(glblfd);
    glblfd=open("raw_1.ts", O_WRONLY | O_TRUNC | O_CREAT, 0666);
    sleep(1);
    r5000_start_stream(usbdev);
    time_t t = time(NULL);
    while (time(NULL) - t < 10)
    {
      // This will timeout after 1ms regardless of data availability
      //usleep(10000);
      r5000_loop_iterate(usbdev, 10);
    }
    r5000_stop_stream(usbdev);
  }
  if(cmd == MULTI_OPEN) {
    printf("Doing multi-open\n");
    while(usbdev) {
      time_t t;
      r5000_start_stream(usbdev);
      t = time(NULL);
      while (time(NULL) - t < 2)
      {
        // This will timeout after 1ms regardless of data availability
        //usleep(10000);
        r5000_loop_iterate(usbdev, 10);
      }
      r5000_stop_stream(usbdev);
      r5000_close(usbdev);
      printf("Closed\n");
      sleep(1);
      usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
      printf("ReOpened %s\n", serial);
    }
    r5000_start_stream(usbdev);
    time_t t = time(NULL);
    while (time(NULL) - t < 10)
    {
      // This will timeout after 1ms regardless of data availability
      //usleep(10000);
      r5000_loop_iterate(usbdev, 10);
    }
    r5000_stop_stream(usbdev);
  }
end:
  r5000_close(usbdev);
  if(ptr != buffer)
    write(glblfd, buffer, ptr-buffer);
  close(glblfd);
  return 0;
}
